/*
 * Copyright (c) 1998, 2013, Oracle and/or its affiliates. All rights reserved.
 * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 *
 */

package com.sun.java.swing.plaf.windows;

import javax.swing.*;
import javax.swing.plaf.ButtonUI;
import javax.swing.plaf.UIResource;

import java.awt.*;
import java.io.Serializable;

import static com.sun.java.swing.plaf.windows.TMSchema.*;
import static com.sun.java.swing.plaf.windows.XPStyle.Skin;

import sun.swing.MenuItemCheckIconFactory;

/**
 * Factory object that can vend Icons appropriate for the Windows L & F.
 * <p>
 * <strong>Warning:</strong>
 * Serialized objects of this class will not be compatible with
 * future Swing releases.  The current serialization support is appropriate
 * for short term storage or RMI between applications running the same
 * version of Swing.  A future release of Swing will provide support for
 * long term persistence.
 *
 * @author David Kloba
 * @author Georges Saab
 * @author Rich Schiavi
 */
public class WindowsIconFactory implements Serializable {

  private static Icon frame_closeIcon;
  private static Icon frame_iconifyIcon;
  private static Icon frame_maxIcon;
  private static Icon frame_minIcon;
  private static Icon frame_resizeIcon;
  private static Icon checkBoxIcon;
  private static Icon radioButtonIcon;
  private static Icon checkBoxMenuItemIcon;
  private static Icon radioButtonMenuItemIcon;
  private static Icon menuItemCheckIcon;
  private static Icon menuItemArrowIcon;
  private static Icon menuArrowIcon;
  private static VistaMenuItemCheckIconFactory menuItemCheckIconFactory;

  public static Icon getMenuItemCheckIcon() {
    if (menuItemCheckIcon == null) {
      menuItemCheckIcon = new MenuItemCheckIcon();
    }
    return menuItemCheckIcon;
  }

  public static Icon getMenuItemArrowIcon() {
    if (menuItemArrowIcon == null) {
      menuItemArrowIcon = new MenuItemArrowIcon();
    }
    return menuItemArrowIcon;
  }

  public static Icon getMenuArrowIcon() {
    if (menuArrowIcon == null) {
      menuArrowIcon = new MenuArrowIcon();
    }
    return menuArrowIcon;
  }

  public static Icon getCheckBoxIcon() {
    if (checkBoxIcon == null) {
      checkBoxIcon = new CheckBoxIcon();
    }
    return checkBoxIcon;
  }

  public static Icon getRadioButtonIcon() {
    if (radioButtonIcon == null) {
      radioButtonIcon = new RadioButtonIcon();
    }
    return radioButtonIcon;
  }

  public static Icon getCheckBoxMenuItemIcon() {
    if (checkBoxMenuItemIcon == null) {
      checkBoxMenuItemIcon = new CheckBoxMenuItemIcon();
    }
    return checkBoxMenuItemIcon;
  }

  public static Icon getRadioButtonMenuItemIcon() {
    if (radioButtonMenuItemIcon == null) {
      radioButtonMenuItemIcon = new RadioButtonMenuItemIcon();
    }
    return radioButtonMenuItemIcon;
  }

  static
  synchronized VistaMenuItemCheckIconFactory getMenuItemCheckIconFactory() {
    if (menuItemCheckIconFactory == null) {
      menuItemCheckIconFactory =
          new VistaMenuItemCheckIconFactory();
    }
    return menuItemCheckIconFactory;
  }

  public static Icon createFrameCloseIcon() {
    if (frame_closeIcon == null) {
      frame_closeIcon = new FrameButtonIcon(Part.WP_CLOSEBUTTON);
    }
    return frame_closeIcon;
  }

  public static Icon createFrameIconifyIcon() {
    if (frame_iconifyIcon == null) {
      frame_iconifyIcon = new FrameButtonIcon(Part.WP_MINBUTTON);
    }
    return frame_iconifyIcon;
  }

  public static Icon createFrameMaximizeIcon() {
    if (frame_maxIcon == null) {
      frame_maxIcon = new FrameButtonIcon(Part.WP_MAXBUTTON);
    }
    return frame_maxIcon;
  }

  public static Icon createFrameMinimizeIcon() {
    if (frame_minIcon == null) {
      frame_minIcon = new FrameButtonIcon(Part.WP_RESTOREBUTTON);
    }
    return frame_minIcon;
  }

  public static Icon createFrameResizeIcon() {
    if (frame_resizeIcon == null) {
      frame_resizeIcon = new ResizeIcon();
    }
    return frame_resizeIcon;
  }


  private static class FrameButtonIcon implements Icon, Serializable {

    private Part part;

    private FrameButtonIcon(Part part) {
      this.part = part;
    }

    public void paintIcon(Component c, Graphics g, int x0, int y0) {
      int width = getIconWidth();
      int height = getIconHeight();

      XPStyle xp = XPStyle.getXP();
      if (xp != null) {
        Skin skin = xp.getSkin(c, part);
        AbstractButton b = (AbstractButton) c;
        ButtonModel model = b.getModel();

        // Find out if frame is inactive
        JInternalFrame jif = (JInternalFrame) SwingUtilities.
            getAncestorOfClass(JInternalFrame.class, b);
        boolean jifSelected = (jif != null && jif.isSelected());

        State state;
        if (jifSelected) {
          if (!model.isEnabled()) {
            state = State.DISABLED;
          } else if (model.isArmed() && model.isPressed()) {
            state = State.PUSHED;
          } else if (model.isRollover()) {
            state = State.HOT;
          } else {
            state = State.NORMAL;
          }
        } else {
          if (!model.isEnabled()) {
            state = State.INACTIVEDISABLED;
          } else if (model.isArmed() && model.isPressed()) {
            state = State.INACTIVEPUSHED;
          } else if (model.isRollover()) {
            state = State.INACTIVEHOT;
          } else {
            state = State.INACTIVENORMAL;
          }
        }
        skin.paintSkin(g, 0, 0, width, height, state);
      } else {
        g.setColor(Color.black);
        int x = width / 12 + 2;
        int y = height / 5;
        int h = height - y * 2 - 1;
        int w = width * 3 / 4 - 3;
        int thickness2 = Math.max(height / 8, 2);
        int thickness = Math.max(width / 15, 1);
        if (part == Part.WP_CLOSEBUTTON) {
          int lineWidth;
          if (width > 47) {
            lineWidth = 6;
          } else if (width > 37) {
            lineWidth = 5;
          } else if (width > 26) {
            lineWidth = 4;
          } else if (width > 16) {
            lineWidth = 3;
          } else if (width > 12) {
            lineWidth = 2;
          } else {
            lineWidth = 1;
          }
          y = height / 12 + 2;
          if (lineWidth == 1) {
            if (w % 2 == 1) {
              x++;
              w++;
            }
            g.drawLine(x, y, x + w - 2, y + w - 2);
            g.drawLine(x + w - 2, y, x, y + w - 2);
          } else if (lineWidth == 2) {
            if (w > 6) {
              x++;
              w--;
            }
            g.drawLine(x, y, x + w - 2, y + w - 2);
            g.drawLine(x + w - 2, y, x, y + w - 2);
            g.drawLine(x + 1, y, x + w - 1, y + w - 2);
            g.drawLine(x + w - 1, y, x + 1, y + w - 2);
          } else {
            x += 2;
            y++;
            w -= 2;
            g.drawLine(x, y, x + w - 1, y + w - 1);
            g.drawLine(x + w - 1, y, x, y + w - 1);
            g.drawLine(x + 1, y, x + w - 1, y + w - 2);
            g.drawLine(x + w - 2, y, x, y + w - 2);
            g.drawLine(x, y + 1, x + w - 2, y + w - 1);
            g.drawLine(x + w - 1, y + 1, x + 1, y + w - 1);
            for (int i = 4; i <= lineWidth; i++) {
              g.drawLine(x + i - 2, y, x + w - 1, y + w - i + 1);
              g.drawLine(x, y + i - 2, x + w - i + 1, y + w - 1);
              g.drawLine(x + w - i + 1, y, x, y + w - i + 1);
              g.drawLine(x + w - 1, y + i - 2, x + i - 2, y + w - 1);
            }
          }
        } else if (part == Part.WP_MINBUTTON) {
          g.fillRect(x, y + h - thickness2, w - w / 3, thickness2);
        } else if (part == Part.WP_MAXBUTTON) {
          g.fillRect(x, y, w, thickness2);
          g.fillRect(x, y, thickness, h);
          g.fillRect(x + w - thickness, y, thickness, h);
          g.fillRect(x, y + h - thickness, w, thickness);
        } else if (part == Part.WP_RESTOREBUTTON) {
          g.fillRect(x + w / 3, y, w - w / 3, thickness2);
          g.fillRect(x + w / 3, y, thickness, h / 3);
          g.fillRect(x + w - thickness, y, thickness, h - h / 3);
          g.fillRect(x + w - w / 3, y + h - h / 3 - thickness, w / 3, thickness);

          g.fillRect(x, y + h / 3, w - w / 3, thickness2);
          g.fillRect(x, y + h / 3, thickness, h - h / 3);
          g.fillRect(x + w - w / 3 - thickness, y + h / 3, thickness, h - h / 3);
          g.fillRect(x, y + h - thickness, w - w / 3, thickness);
        }
      }
    }

    public int getIconWidth() {
      int width;
      if (XPStyle.getXP() != null) {
        // Fix for XP bug where sometimes these sizes aren't updated properly
        // Assume for now that height is correct and derive width using the
        // ratio from the uxtheme part
        width = UIManager.getInt("InternalFrame.titleButtonHeight") - 2;
        Dimension d = XPStyle.getPartSize(Part.WP_CLOSEBUTTON, State.NORMAL);
        if (d != null && d.width != 0 && d.height != 0) {
          width = (int) ((float) width * d.width / d.height);
        }
      } else {
        width = UIManager.getInt("InternalFrame.titleButtonWidth") - 2;
      }
      if (XPStyle.getXP() != null) {
        width -= 2;
      }
      return width;
    }

    public int getIconHeight() {
      int height = UIManager.getInt("InternalFrame.titleButtonHeight") - 4;
      return height;
    }
  }


  private static class ResizeIcon implements Icon, Serializable {

    public void paintIcon(Component c, Graphics g, int x, int y) {
      g.setColor(UIManager.getColor("InternalFrame.resizeIconHighlight"));
      g.drawLine(0, 11, 11, 0);
      g.drawLine(4, 11, 11, 4);
      g.drawLine(8, 11, 11, 8);

      g.setColor(UIManager.getColor("InternalFrame.resizeIconShadow"));
      g.drawLine(1, 11, 11, 1);
      g.drawLine(2, 11, 11, 2);
      g.drawLine(5, 11, 11, 5);
      g.drawLine(6, 11, 11, 6);
      g.drawLine(9, 11, 11, 9);
      g.drawLine(10, 11, 11, 10);
    }

    public int getIconWidth() {
      return 13;
    }

    public int getIconHeight() {
      return 13;
    }
  }

  ;

  private static class CheckBoxIcon implements Icon, Serializable {

    final static int csize = 13;

    public void paintIcon(Component c, Graphics g, int x, int y) {
      JCheckBox cb = (JCheckBox) c;
      ButtonModel model = cb.getModel();
      XPStyle xp = XPStyle.getXP();

      if (xp != null) {
        State state;
        if (model.isSelected()) {
          state = State.CHECKEDNORMAL;
          if (!model.isEnabled()) {
            state = State.CHECKEDDISABLED;
          } else if (model.isPressed() && model.isArmed()) {
            state = State.CHECKEDPRESSED;
          } else if (model.isRollover()) {
            state = State.CHECKEDHOT;
          }
        } else {
          state = State.UNCHECKEDNORMAL;
          if (!model.isEnabled()) {
            state = State.UNCHECKEDDISABLED;
          } else if (model.isPressed() && model.isArmed()) {
            state = State.UNCHECKEDPRESSED;
          } else if (model.isRollover()) {
            state = State.UNCHECKEDHOT;
          }
        }
        Part part = Part.BP_CHECKBOX;
        xp.getSkin(c, part).paintSkin(g, x, y, state);
      } else {
        // outer bevel
        if (!cb.isBorderPaintedFlat()) {
          // Outer top/left
          g.setColor(UIManager.getColor("CheckBox.shadow"));
          g.drawLine(x, y, x + 11, y);
          g.drawLine(x, y + 1, x, y + 11);

          // Outer bottom/right
          g.setColor(UIManager.getColor("CheckBox.highlight"));
          g.drawLine(x + 12, y, x + 12, y + 12);
          g.drawLine(x, y + 12, x + 11, y + 12);

          // Inner top.left
          g.setColor(UIManager.getColor("CheckBox.darkShadow"));
          g.drawLine(x + 1, y + 1, x + 10, y + 1);
          g.drawLine(x + 1, y + 2, x + 1, y + 10);

          // Inner bottom/right
          g.setColor(UIManager.getColor("CheckBox.light"));
          g.drawLine(x + 1, y + 11, x + 11, y + 11);
          g.drawLine(x + 11, y + 1, x + 11, y + 10);

          // inside box
          if ((model.isPressed() && model.isArmed()) || !model.isEnabled()) {
            g.setColor(UIManager.getColor("CheckBox.background"));
          } else {
            g.setColor(UIManager.getColor("CheckBox.interiorBackground"));
          }
          g.fillRect(x + 2, y + 2, csize - 4, csize - 4);
        } else {
          g.setColor(UIManager.getColor("CheckBox.shadow"));
          g.drawRect(x + 1, y + 1, csize - 3, csize - 3);

          if ((model.isPressed() && model.isArmed()) || !model.isEnabled()) {
            g.setColor(UIManager.getColor("CheckBox.background"));
          } else {
            g.setColor(UIManager.getColor("CheckBox.interiorBackground"));
          }
          g.fillRect(x + 2, y + 2, csize - 4, csize - 4);
        }

        if (model.isEnabled()) {
          g.setColor(UIManager.getColor("CheckBox.foreground"));
        } else {
          g.setColor(UIManager.getColor("CheckBox.shadow"));
        }

        // paint check
        if (model.isSelected()) {
          g.drawLine(x + 9, y + 3, x + 9, y + 3);
          g.drawLine(x + 8, y + 4, x + 9, y + 4);
          g.drawLine(x + 7, y + 5, x + 9, y + 5);
          g.drawLine(x + 6, y + 6, x + 8, y + 6);
          g.drawLine(x + 3, y + 7, x + 7, y + 7);
          g.drawLine(x + 4, y + 8, x + 6, y + 8);
          g.drawLine(x + 5, y + 9, x + 5, y + 9);
          g.drawLine(x + 3, y + 5, x + 3, y + 5);
          g.drawLine(x + 3, y + 6, x + 4, y + 6);
        }
      }
    }

    public int getIconWidth() {
      XPStyle xp = XPStyle.getXP();
      if (xp != null) {
        return xp.getSkin(null, Part.BP_CHECKBOX).getWidth();
      } else {
        return csize;
      }
    }

    public int getIconHeight() {
      XPStyle xp = XPStyle.getXP();
      if (xp != null) {
        return xp.getSkin(null, Part.BP_CHECKBOX).getHeight();
      } else {
        return csize;
      }
    }
  }

  private static class RadioButtonIcon implements Icon, UIResource, Serializable {

    public void paintIcon(Component c, Graphics g, int x, int y) {
      AbstractButton b = (AbstractButton) c;
      ButtonModel model = b.getModel();
      XPStyle xp = XPStyle.getXP();

      if (xp != null) {
        Part part = Part.BP_RADIOBUTTON;
        Skin skin = xp.getSkin(b, part);
        State state;
        int index = 0;
        if (model.isSelected()) {
          state = State.CHECKEDNORMAL;
          if (!model.isEnabled()) {
            state = State.CHECKEDDISABLED;
          } else if (model.isPressed() && model.isArmed()) {
            state = State.CHECKEDPRESSED;
          } else if (model.isRollover()) {
            state = State.CHECKEDHOT;
          }
        } else {
          state = State.UNCHECKEDNORMAL;
          if (!model.isEnabled()) {
            state = State.UNCHECKEDDISABLED;
          } else if (model.isPressed() && model.isArmed()) {
            state = State.UNCHECKEDPRESSED;
          } else if (model.isRollover()) {
            state = State.UNCHECKEDHOT;
          }
        }
        skin.paintSkin(g, x, y, state);
      } else {
        // fill interior
        if ((model.isPressed() && model.isArmed()) || !model.isEnabled()) {
          g.setColor(UIManager.getColor("RadioButton.background"));
        } else {
          g.setColor(UIManager.getColor("RadioButton.interiorBackground"));
        }
        g.fillRect(x + 2, y + 2, 8, 8);

        // outter left arc
        g.setColor(UIManager.getColor("RadioButton.shadow"));
        g.drawLine(x + 4, y + 0, x + 7, y + 0);
        g.drawLine(x + 2, y + 1, x + 3, y + 1);
        g.drawLine(x + 8, y + 1, x + 9, y + 1);
        g.drawLine(x + 1, y + 2, x + 1, y + 3);
        g.drawLine(x + 0, y + 4, x + 0, y + 7);
        g.drawLine(x + 1, y + 8, x + 1, y + 9);

        // outter right arc
        g.setColor(UIManager.getColor("RadioButton.highlight"));
        g.drawLine(x + 2, y + 10, x + 3, y + 10);
        g.drawLine(x + 4, y + 11, x + 7, y + 11);
        g.drawLine(x + 8, y + 10, x + 9, y + 10);
        g.drawLine(x + 10, y + 9, x + 10, y + 8);
        g.drawLine(x + 11, y + 7, x + 11, y + 4);
        g.drawLine(x + 10, y + 3, x + 10, y + 2);

        // inner left arc
        g.setColor(UIManager.getColor("RadioButton.darkShadow"));
        g.drawLine(x + 4, y + 1, x + 7, y + 1);
        g.drawLine(x + 2, y + 2, x + 3, y + 2);
        g.drawLine(x + 8, y + 2, x + 9, y + 2);
        g.drawLine(x + 2, y + 3, x + 2, y + 3);
        g.drawLine(x + 1, y + 4, x + 1, y + 7);
        g.drawLine(x + 2, y + 8, x + 2, y + 8);

        // inner right arc
        g.setColor(UIManager.getColor("RadioButton.light"));
        g.drawLine(x + 2, y + 9, x + 3, y + 9);
        g.drawLine(x + 4, y + 10, x + 7, y + 10);
        g.drawLine(x + 8, y + 9, x + 9, y + 9);
        g.drawLine(x + 9, y + 8, x + 9, y + 8);
        g.drawLine(x + 10, y + 7, x + 10, y + 4);
        g.drawLine(x + 9, y + 3, x + 9, y + 3);

        // indicate whether selected or not
        if (model.isSelected()) {
          if (model.isEnabled()) {
            g.setColor(UIManager.getColor("RadioButton.foreground"));
          } else {
            g.setColor(UIManager.getColor("RadioButton.shadow"));
          }
          g.fillRect(x + 4, y + 5, 4, 2);
          g.fillRect(x + 5, y + 4, 2, 4);
        }
      }
    }

    public int getIconWidth() {
      XPStyle xp = XPStyle.getXP();
      if (xp != null) {
        return xp.getSkin(null, Part.BP_RADIOBUTTON).getWidth();
      } else {
        return 13;
      }
    }

    public int getIconHeight() {
      XPStyle xp = XPStyle.getXP();
      if (xp != null) {
        return xp.getSkin(null, Part.BP_RADIOBUTTON).getHeight();
      } else {
        return 13;
      }
    }
  } // end class RadioButtonIcon


  private static class CheckBoxMenuItemIcon implements Icon, UIResource, Serializable {

    public void paintIcon(Component c, Graphics g, int x, int y) {
      AbstractButton b = (AbstractButton) c;
      ButtonModel model = b.getModel();
      boolean isSelected = model.isSelected();
      if (isSelected) {
        y = y - getIconHeight() / 2;
        g.drawLine(x + 9, y + 3, x + 9, y + 3);
        g.drawLine(x + 8, y + 4, x + 9, y + 4);
        g.drawLine(x + 7, y + 5, x + 9, y + 5);
        g.drawLine(x + 6, y + 6, x + 8, y + 6);
        g.drawLine(x + 3, y + 7, x + 7, y + 7);
        g.drawLine(x + 4, y + 8, x + 6, y + 8);
        g.drawLine(x + 5, y + 9, x + 5, y + 9);
        g.drawLine(x + 3, y + 5, x + 3, y + 5);
        g.drawLine(x + 3, y + 6, x + 4, y + 6);
      }
    }

    public int getIconWidth() {
      return 9;
    }

    public int getIconHeight() {
      return 9;
    }

  } // End class CheckBoxMenuItemIcon


  private static class RadioButtonMenuItemIcon implements Icon, UIResource, Serializable {

    public void paintIcon(Component c, Graphics g, int x, int y) {
      AbstractButton b = (AbstractButton) c;
      ButtonModel model = b.getModel();
      if (b.isSelected() == true) {
        g.fillRoundRect(x + 3, y + 3, getIconWidth() - 6, getIconHeight() - 6,
            4, 4);
      }
    }

    public int getIconWidth() {
      return 12;
    }

    public int getIconHeight() {
      return 12;
    }

  } // End class RadioButtonMenuItemIcon


  private static class MenuItemCheckIcon implements Icon, UIResource, Serializable {

    public void paintIcon(Component c, Graphics g, int x, int y) {
            /* For debugging:
               Color oldColor = g.getColor();
            g.setColor(Color.orange);
            g.fill3DRect(x,y,getIconWidth(), getIconHeight(), true);
            g.setColor(oldColor);
            */
    }

    public int getIconWidth() {
      return 9;
    }

    public int getIconHeight() {
      return 9;
    }

  } // End class MenuItemCheckIcon

  private static class MenuItemArrowIcon implements Icon, UIResource, Serializable {

    public void paintIcon(Component c, Graphics g, int x, int y) {
            /* For debugging:
            Color oldColor = g.getColor();
            g.setColor(Color.green);
            g.fill3DRect(x,y,getIconWidth(), getIconHeight(), true);
            g.setColor(oldColor);
            */
    }

    public int getIconWidth() {
      return 4;
    }

    public int getIconHeight() {
      return 8;
    }

  } // End class MenuItemArrowIcon

  private static class MenuArrowIcon implements Icon, UIResource, Serializable {

    public void paintIcon(Component c, Graphics g, int x, int y) {
      XPStyle xp = XPStyle.getXP();
      if (WindowsMenuItemUI.isVistaPainting(xp)) {
        State state = State.NORMAL;
        if (c instanceof JMenuItem) {
          state = ((JMenuItem) c).getModel().isEnabled()
              ? State.NORMAL : State.DISABLED;
        }
        Skin skin = xp.getSkin(c, Part.MP_POPUPSUBMENU);
        if (WindowsGraphicsUtils.isLeftToRight(c)) {
          skin.paintSkin(g, x, y, state);
        } else {
          Graphics2D g2d = (Graphics2D) g.create();
          g2d.translate(x + skin.getWidth(), y);
          g2d.scale(-1, 1);
          skin.paintSkin(g2d, 0, 0, state);
          g2d.dispose();
        }
      } else {
        g.translate(x, y);
        if (WindowsGraphicsUtils.isLeftToRight(c)) {
          g.drawLine(0, 0, 0, 7);
          g.drawLine(1, 1, 1, 6);
          g.drawLine(2, 2, 2, 5);
          g.drawLine(3, 3, 3, 4);
        } else {
          g.drawLine(4, 0, 4, 7);
          g.drawLine(3, 1, 3, 6);
          g.drawLine(2, 2, 2, 5);
          g.drawLine(1, 3, 1, 4);
        }
        g.translate(-x, -y);
      }
    }

    public int getIconWidth() {
      XPStyle xp = XPStyle.getXP();
      if (WindowsMenuItemUI.isVistaPainting(xp)) {
        Skin skin = xp.getSkin(null, Part.MP_POPUPSUBMENU);
        return skin.getWidth();
      } else {
        return 4;
      }
    }

    public int getIconHeight() {
      XPStyle xp = XPStyle.getXP();
      if (WindowsMenuItemUI.isVistaPainting(xp)) {
        Skin skin = xp.getSkin(null, Part.MP_POPUPSUBMENU);
        return skin.getHeight();
      } else {
        return 8;
      }
    }
  } // End class MenuArrowIcon

  static class VistaMenuItemCheckIconFactory
      implements MenuItemCheckIconFactory {

    private static final int OFFSET = 3;

    public Icon getIcon(JMenuItem component) {
      return new VistaMenuItemCheckIcon(component);
    }

    public boolean isCompatible(Object icon, String prefix) {
      return icon instanceof VistaMenuItemCheckIcon
          && ((VistaMenuItemCheckIcon) icon).type == getType(prefix);
    }

    public Icon getIcon(String type) {
      return new VistaMenuItemCheckIcon(type);
    }

    static int getIconWidth() {
      XPStyle xp = XPStyle.getXP();
      return ((xp != null) ? xp.getSkin(null, Part.MP_POPUPCHECK).getWidth() : 16)
          + 2 * OFFSET;
    }

    private static Class<? extends JMenuItem> getType(Component c) {
      Class<? extends JMenuItem> rv = null;
      if (c instanceof JCheckBoxMenuItem) {
        rv = JCheckBoxMenuItem.class;
      } else if (c instanceof JRadioButtonMenuItem) {
        rv = JRadioButtonMenuItem.class;
      } else if (c instanceof JMenu) {
        rv = JMenu.class;
      } else if (c instanceof JMenuItem) {
        rv = JMenuItem.class;
      }
      return rv;
    }

    private static Class<? extends JMenuItem> getType(String type) {
      Class<? extends JMenuItem> rv = null;
      if (type == "CheckBoxMenuItem") {
        rv = JCheckBoxMenuItem.class;
      } else if (type == "RadioButtonMenuItem") {
        rv = JRadioButtonMenuItem.class;
      } else if (type == "Menu") {
        rv = JMenu.class;
      } else if (type == "MenuItem") {
        rv = JMenuItem.class;
      } else {
        // this should never happen
        rv = JMenuItem.class;
      }
      return rv;
    }

    /**
     * CheckIcon for JMenuItem, JMenu, JCheckBoxMenuItem and
     * JRadioButtonMenuItem.
     * Note: to be used on Vista only.
     */
    private static class VistaMenuItemCheckIcon
        implements Icon, UIResource, Serializable {

      private final JMenuItem menuItem;
      private final Class<? extends JMenuItem> type;

      VistaMenuItemCheckIcon(JMenuItem menuItem) {
        this.type = getType(menuItem);
        this.menuItem = menuItem;
      }

      VistaMenuItemCheckIcon(String type) {
        this.type = getType(type);
        this.menuItem = null;
      }

      public int getIconHeight() {
        Icon lafIcon = getLaFIcon();
        if (lafIcon != null) {
          return lafIcon.getIconHeight();
        }
        Icon icon = getIcon();
        int height = 0;
        if (icon != null) {
          height = icon.getIconHeight();
        } else {
          XPStyle xp = XPStyle.getXP();
          if (xp != null) {
            Skin skin = xp.getSkin(null, Part.MP_POPUPCHECK);
            height = skin.getHeight();
          } else {
            height = 16;
          }
        }
        height += 2 * OFFSET;
        return height;
      }

      public int getIconWidth() {
        Icon lafIcon = getLaFIcon();
        if (lafIcon != null) {
          return lafIcon.getIconWidth();
        }
        Icon icon = getIcon();
        int width = 0;
        if (icon != null) {
          width = icon.getIconWidth() + 2 * OFFSET;
        } else {
          width = VistaMenuItemCheckIconFactory.getIconWidth();
        }
        return width;
      }

      public void paintIcon(Component c, Graphics g, int x, int y) {
        Icon lafIcon = getLaFIcon();
        if (lafIcon != null) {
          lafIcon.paintIcon(c, g, x, y);
          return;
        }
        assert menuItem == null || c == menuItem;
        Icon icon = getIcon();
        if (type == JCheckBoxMenuItem.class
            || type == JRadioButtonMenuItem.class) {
          AbstractButton b = (AbstractButton) c;
          if (b.isSelected()) {
            Part backgroundPart = Part.MP_POPUPCHECKBACKGROUND;
            Part part = Part.MP_POPUPCHECK;
            State backgroundState;
            State state;
            if (isEnabled(c, null)) {
              backgroundState =
                  (icon != null) ? State.BITMAP : State.NORMAL;
              state = (type == JRadioButtonMenuItem.class)
                  ? State.BULLETNORMAL
                  : State.CHECKMARKNORMAL;
            } else {
              backgroundState = State.DISABLEDPUSHED;
              state =
                  (type == JRadioButtonMenuItem.class)
                      ? State.BULLETDISABLED
                      : State.CHECKMARKDISABLED;
            }
            XPStyle xp = XPStyle.getXP();
            if (xp != null) {
              Skin skin;
              skin = xp.getSkin(c, backgroundPart);
              skin.paintSkin(g, x, y,
                  getIconWidth(), getIconHeight(), backgroundState);
              if (icon == null) {
                skin = xp.getSkin(c, part);
                skin.paintSkin(g, x + OFFSET, y + OFFSET, state);
              }
            }
          }
        }
        if (icon != null) {
          icon.paintIcon(c, g, x + OFFSET, y + OFFSET);
        }
      }

      private static WindowsMenuItemUIAccessor getAccessor(
          JMenuItem menuItem) {
        WindowsMenuItemUIAccessor rv = null;
        ButtonUI uiObject = (menuItem != null) ? menuItem.getUI()
            : null;
        if (uiObject instanceof WindowsMenuItemUI) {
          rv = ((WindowsMenuItemUI) uiObject).accessor;
        } else if (uiObject instanceof WindowsMenuUI) {
          rv = ((WindowsMenuUI) uiObject).accessor;
        } else if (uiObject instanceof WindowsCheckBoxMenuItemUI) {
          rv = ((WindowsCheckBoxMenuItemUI) uiObject).accessor;
        } else if (uiObject instanceof WindowsRadioButtonMenuItemUI) {
          rv = ((WindowsRadioButtonMenuItemUI) uiObject).accessor;
        }
        return rv;
      }

      private static boolean isEnabled(Component c, State state) {
        if (state == null && c instanceof JMenuItem) {
          WindowsMenuItemUIAccessor accessor =
              getAccessor((JMenuItem) c);
          if (accessor != null) {
            state = accessor.getState((JMenuItem) c);
          }
        }
        if (state == null) {
          if (c != null) {
            return c.isEnabled();
          } else {
            return true;
          }
        } else {
          return (state != State.DISABLED)
              && (state != State.DISABLEDHOT)
              && (state != State.DISABLEDPUSHED);
        }
      }

      private Icon getIcon() {
        Icon rv = null;
        if (menuItem == null) {
          return rv;
        }
        WindowsMenuItemUIAccessor accessor =
            getAccessor(menuItem);
        State state = (accessor != null) ? accessor.getState(menuItem)
            : null;
        if (isEnabled(menuItem, null)) {
          if (state == State.PUSHED) {
            rv = menuItem.getPressedIcon();
          } else {
            rv = menuItem.getIcon();
          }
        } else {
          rv = menuItem.getDisabledIcon();
        }
        return rv;
      }

      /**
       * Check if developer changed icon in the UI table.
       *
       * @return the icon to use or {@code null} if the current one is to be used
       */
      private Icon getLaFIcon() {
        // use icon from the UI table if it does not match this one.
        Icon rv = (Icon) UIManager.getDefaults().get(typeToString(type));
        if (rv instanceof VistaMenuItemCheckIcon
            && ((VistaMenuItemCheckIcon) rv).type == type) {
          rv = null;
        }
        return rv;
      }

      private static String typeToString(
          Class<? extends JMenuItem> type) {
        assert type == JMenuItem.class
            || type == JMenu.class
            || type == JCheckBoxMenuItem.class
            || type == JRadioButtonMenuItem.class;
        StringBuilder sb = new StringBuilder(type.getName());
        // remove package name, dot and the first character
        sb.delete(0, sb.lastIndexOf("J") + 1);
        sb.append(".checkIcon");
        return sb.toString();
      }
    }
  } // End class VistaMenuItemCheckIconFactory
}
